/*
 *===========================================================
 *|  __        ___ _   _   _____ _                          |
 *|  \ \      / (_) |_| |_|_   _(_)_ __ ___   ___           |
 *|   \ \ /\ / /| | __| '_ \| | | | '_ ` _ \ / _ \          |
 *|    \ V  V / | | |_| | | | | | | | | | | |  __/          |
 *|     \_/\_/  |_|\__|_| |_|_| |_|_| |_| |_|\___|          |
 *|                                                         |
 *|                                                         |
 *| Copyright by TianJin YuShi Data Technology Co., Ltd.    |
 *===========================================================
 */

package jaux.message.codec.utils;

/**
 * Created by Mihajlovic on 5/27/16.
 */

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

public class ByteUtil {

    /**
     * Get hex string format of a byte array.
     *
     * @param byteArray
     * @return
     */
    public static String getHexString(byte[] byteArray) {
        return getHexString(byteArray, byteArray.length);
    }

    public static String getHexString(byte[] byteArray, int length) {
        return getHexString(byteArray, 0, length);
    }

    public static String getHexString(byte[] byteArray, int startIndex, int length) {
        String result = "";

        for (int i = startIndex; i < startIndex + length; i++) {
            String tmp = Integer.toHexString((int) (byteArray[i] & 0xff)).toUpperCase();
            if (tmp.length() == 1) {
                tmp = "0" + tmp;
            }
            result = result + tmp + " ";
        }
        return result;
    }

    public static String getHexString(byte[] byteArray, int startIndex, int length, String separator) {
        String result = "";

        for (int i = startIndex; i < startIndex + length; i++) {
            String tmp = Integer.toHexString((int) (byteArray[i] & 0xff)).toUpperCase();
            if (tmp.length() == 1) {
                tmp = "0" + tmp;
            }
            result = result + tmp + separator;
        }
        return result;
    }


    public static byte[] standard12bitArray(byte[] data, int isFirstHalf) {
        String datastr = getHexString(data, 0, data.length, "");
        if (isFirstHalf == 0) {
            datastr = datastr.substring(1);
        }

        List<String> buf = new ArrayList<>();
        for (int i = 0; i < datastr.length(); i++) {
            if (i % 3 == 0) {
                buf.add("0");
            }
            buf.add("" + datastr.charAt(i));
        }

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < buf.size(); i = i + 4) {
            if (i + 3 >= buf.size()) {
                break;
            }
            sb.append(buf.get(i));
            sb.append(buf.get(i + 1));
            sb.append(buf.get(i + 2));
            sb.append(buf.get(i + 3));

        }

        return parseBytes(sb.toString(), "");
    }

    /**
     * convert hex string to byte[]
     *
     * @param byteString hex string
     * @param separator
     * @return
     */
    public static byte[] parseBytes(String byteString, String separator) {
        byte[] result;
        String[] bytes;
        if (separator == null || separator.equals("")) {
            List<String> tmpList = new ArrayList<String>();
            for (int i = 0; i < byteString.length(); i += 2) {
                if (i + 2 < byteString.length()) {
                    tmpList.add(byteString.substring(i, i + 2));
                } else {
                    tmpList.add(byteString.substring(i, byteString.length()));
                }
            }
            bytes = new String[tmpList.size()];
            bytes = tmpList.toArray(bytes);
        } else {
            bytes = byteString.split(separator);
        }

        result = new byte[bytes.length];
        for (int i = 0; i < bytes.length; i++) {
            result[i] = (byte) (Integer.parseUnsignedInt(bytes[i], 16));
        }
        return result;
    }

    /**
     * Concat two byte arrays.
     *
     * @param arrLeft
     * @param arrRight
     * @return
     */
    public static byte[] concat(byte[] arrLeft, byte[] arrRight) {
        byte[] result = new byte[arrLeft.length + arrRight.length];
        System.arraycopy(arrLeft, 0, result, 0, arrLeft.length);
        System.arraycopy(arrRight, 0, result, arrLeft.length, arrRight.length);
        return result;
    }

    public static boolean isEquals(byte[] array1, byte[] array2) {
        return Arrays.equals(array1, array2);
    }

    /**
     * Convert a byte to unsigned int
     *
     * @param b
     * @return
     */
    public static int byteToInt(byte b) {
        return 0xFF & (byte) b;
    }

    public static int byteToIntSign(byte b) {
        return b;
    }

    public static byte intToByte(int value) {
        return (byte) (0xff & value);
    }

    /**
     * Convert two byte to unsigned int, high byte first
     *
     * @param word
     * @return
     */
    public static int wordToIntHighFirst(byte[] word) {
        if (word.length != 2) {
            return -1;
        }
        return ((word[0] & 0xff) << 8) | (word[1] & 0xff);
    }

    public static int firstHalfWordToInt(byte[] word) {
        if (word.length != 2) {
            return -1;
        }
        return (((word[0] & 0xf0) >>> 4) << 8) | ((word[0] & 0x0f) << 4 | (word[1] & 0xf0) >>> 4);
    }

    public static int lastHalfWordToInt(byte[] word) {
        if (word.length != 2) {
            return -1;
        }
        return ((word[0] & 0x0f) << 8) | (word[1] & 0xff);
    }

    public static int wordToIntHighFirstSign(byte[] word) {
        if (word.length != 2) {
            return -1;
        }
        return ((word[0] << 8) | (word[1] & 0xff));
    }

    /**
     * Convert four bytes to unsigned int, high byte first
     */
    public static int doubleToIntHighFirst(byte[] doublebytes) {
        if (doublebytes.length != 4) {
            return -1;
        }
        return ((doublebytes[0] & 0xff) << 24 | (doublebytes[1] & 0xff) << 16 | (doublebytes[2] & 0xff) << 8 | (doublebytes[3] & 0xff));
    }

    /**
     * Convert four bytes to int, high byte first
     */
    public static int doubleToIntHighFirstSign(byte[] doublebytes) {
        if (doublebytes.length != 4) {
            return -1;
        }
        return (doublebytes[0] << 24 | (doublebytes[1] & 0xff) << 16 | (doublebytes[2] & 0xff) << 8 | (doublebytes[3] & 0xff));
    }

    /**
     * Convert two byte to unsigned int, low byte first
     *
     * @param word
     * @return
     */
    public static int wordToIntLowFirst(byte[] word) {
        if (word.length != 2) {
            return -1;
        }
        return ((word[1] & 0xff) << 8) | (word[0] & 0xff);
    }


    /**
     * Convert four bytes to unsigned int, low byte first
     */
    public static int doubleToIntLowFirst(byte[] doublebytes) {
        if (doublebytes.length != 4) {
            return -1;
        }
        return ((doublebytes[3] & 0xff) << 24 | (doublebytes[2] & 0xff) << 16 | (doublebytes[1] & 0xff) << 8 | (doublebytes[0] & 0xff));
    }

    public static int doubleToIntLow16First(byte[] doublebytes) {
        if (doublebytes.length != 4) {
            return -1;
        }
        return ((doublebytes[2] & 0xff) << 24 | (doublebytes[3] & 0xff) << 16 | (doublebytes[0] & 0xff) << 8 | (doublebytes[1] & 0xff));
    }

    /**
     * Convert int value to byte array, high byte first.
     *
     * @param value
     * @return
     */
    public static byte[] intToWordHighFirst(int value) {
        byte[] result = new byte[2];
        result[0] = (byte) (value >> 8 & 0xff);
        result[1] = (byte) (value & 0xff);
        return result;
    }

    /**
     * Convert int value to byte array, low byte first.
     *
     * @param value
     * @return
     */
    public static byte[] intToWordLowFirst(int value) {
        byte[] result = new byte[2];
        result[1] = (byte) (value >> 8 & 0xff);
        result[0] = (byte) (value & 0x00ff);
        return result;
    }

    /**
     * Concat a series byte array
     *
     * @param arrays
     * @return
     */
    public static byte[] concat(byte[]... arrays) {
        int totalLength = 0;
        for (int i = 0; i < arrays.length; i++) {
            totalLength = totalLength + arrays[i].length;
        }
        byte[] result = new byte[totalLength];
        int current_index = 0;
        for (int i = 0; i < arrays.length; i++) {
            for (int j = 0; j < arrays[i].length; j++) {
                result[current_index] = arrays[i][j];
                current_index = current_index + j;
            }
        }
        return result;
    }

    public static int halfByteToInt(byte b, boolean isFirstHalf) {
        if (isFirstHalf) {
            return (0xF0 & (byte) b) >>> 4 & 0xFF;
        } else {
            return (0x0F & (byte) b) & 0xFF;
        }
    }

    /**
     * @param originBytes 内容
     * @param beginIndex  包含
     * @param endIndex    包含
     * @return
     */
    public static byte[] subBytes(byte[] originBytes, int beginIndex, int endIndex) {
        return Arrays.copyOfRange(originBytes, beginIndex, endIndex + 1);


    }
    /**
     * Convert byte to bit array.
     *
     * @param b the b
     * @return the integer[]
     */
    public static int[] convertByteToBitArray(byte b) {
        return new int[]{((b >> 7) & 0x1), ((b >> 6) & 0x1),
             ((b >> 5) & 0x1), ((b >> 4) & 0x1),
             ((b >> 3) & 0x1), ((b >> 2) & 0x1),
             ((b >> 1) & 0x1), ((b >> 0) & 0x1)};
    }
    
    /**
     * Convert byte to bit array.
     *
     * @param bs the bs
     * @return the integer[]
     */
    public static int[] convertBytesToBitArray(byte[] bs) {
        int[] result = new int[bs.length*8];
        for (int i = 0; i < bs.length; i++) {
        	int[] ints = convertByteToBitArray(bs[i]);
        	for(int ii=0;ii<8;ii++) {
        		result[i+ii] = ints[ii];
        	}
        }
        return result;
    }
}
